package com.zck.thumbup.aop;

import com.zck.thumbup.exception.BusinessException;
import com.zck.thumbup.exception.ErrorCode;
import com.zck.thumbup.lock.RedisLock;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Redis分布式锁切面 - 支持SpEL表达式
 */
@Slf4j
@Aspect
@Component
public class RedisLockAspect {

    @Resource
    private RedissonClient redissonClient;

    private final ExpressionParser parser = new SpelExpressionParser();

    @Around("@annotation(redisLock)")
    public Object around(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {
        // 解析SpEL表达式
        String lockKey = parseSpEL(joinPoint, redisLock.key());

        RLock lock = redissonClient.getLock(lockKey);
        boolean isLocked = false;

        try {
            isLocked = lock.tryLock(
                    redisLock.waitTime(),
                    redisLock.leaseTime(),
                    redisLock.timeUnit()
            );

            if (!isLocked) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "获取锁超时");
            }

            return joinPoint.proceed();

        } finally {
            if (isLocked && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 解析SpEL表达式
     */
    private String parseSpEL(ProceedingJoinPoint joinPoint, String key) {
        try {
            // 如果key不包含SpEL表达式（没有#号），直接返回原key
            if (!key.contains("#")) {
                return key;
            }

            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            Object[] args = joinPoint.getArgs();
            String[] paramNames = signature.getParameterNames();

            StandardEvaluationContext context = new StandardEvaluationContext();

            // 将方法参数绑定到上下文[5](@ref)
            if (paramNames != null) {
                for (int i = 0; i < paramNames.length; i++) {
                    context.setVariable(paramNames[i], args[i]);
                }
            }

            // 使用正则表达式提取SpEL表达式部分并进行替换
            Pattern pattern = Pattern.compile("#[a-zA-Z0-9._]+");
            Matcher matcher = pattern.matcher(key);
            StringBuffer result = new StringBuffer();
            ExpressionParser parser = new SpelExpressionParser();

            while (matcher.find()) {
                String spelExpression = matcher.group(); // 如 "#doThumbRequest.blogId"
                Expression expression = parser.parseExpression(spelExpression);
                Object value = expression.getValue(context, Object.class);
                // 将SpEL表达式替换为实际值
                matcher.appendReplacement(result, value != null ? value.toString() : "");
            }
            matcher.appendTail(result);

            return result.toString();

        } catch (Exception e) {
            log.error("SpEL表达式解析失败: key={}", key, e);
            throw new RuntimeException("加锁失败!请联系管理员");
        }
    }
}